using System;
using System.IO;
using System.Reflection;
using System.Resources;

namespace BoxBinary.Foundations.OperationalManagement.Resources
{
    public class NonLocalizedResourceReader
    {
        #region Constructors

        private static NonLocalizedResourceReader _default;

        /// <summary>
        /// Default constructor marked as private so object cannot be constructed
        /// </summary>
        private NonLocalizedResourceReader()
        {
        }

        public static NonLocalizedResourceReader Default
        {
            get
            {
                if (_default == null)
                    _default = new NonLocalizedResourceReader();
                return _default;
            }
        }

        #endregion

        #region Methods

        #region Public Static Methods

        /// <summary>
        /// Returns the default namespace for finding resources. When overriden in a derived class, alters the location of resources
        /// loaded by <see cref="M:GetString(string resourceNamespaceStub)"/>.
        /// </summary>
        public string DefaultRootNamespace
        {
            get { return "CondeNetUK.FoundationServices"; }
        }

        /// <summary>
        /// Gets the resource with the supplied name from the Foundation Services assembly's resources.
        /// </summary>
        /// <param name="resourceNamespaceStub">The partially qualified name of the resource to fetch.</param>
        /// <returns>A stream of the resource data if found, otherwise null</returns>
        /// <exception cref="System.ArgumentNullException">Thrown when supplied parameters are null.</exception>
        public Stream GetStream(string resourceNamespaceStub)
        {
            #region Validate

            if (resourceNamespaceStub == null)
                throw new ArgumentNullException("resourceNamespaceStub");

            #endregion

            // Get the currently executing assembly and then return the resource string
            return GetStream(DefaultRootNamespace, resourceNamespaceStub);
        }

        /// <summary>
        /// Gets the resource with the supplied name from the Foundation Services assembly's resources.
        /// </summary>
        /// <param name="overrideDefaultNamespace">The partially qualified root namespace to search.</param>
        /// <param name="resourceNamespaceStub">The partially qualified name of the resource to fetch.</param>
        /// <returns>A stream of the resource data if found, otherwise null</returns>
        /// <exception cref="System.ArgumentNullException">Thrown when supplied parameters are null.</exception>
        public Stream GetStream(string overrideDefaultNamespace, string resourceNamespaceStub)
        {
            #region Validate

            if (resourceNamespaceStub == null)
                throw new ArgumentNullException("resourceNamespaceStub");

            #endregion

            // Get the currently executing assembly and then return the resource string
            return GetStream(overrideDefaultNamespace, resourceNamespaceStub, Assembly.GetExecutingAssembly());
        }

        /// <summary>
        /// Gets the resource with the supplied name from the supplied assembly's resources.
        /// </summary>
        /// <param name="overrideDefaultNamespace">The partially qualified root namespace to search.</param>
        /// <param name="resourceNamespaceStub">The partially qualified name of the resource to fetch.</param>
        /// <param name="assembly">The assembly to fetch the resource from.</param>
        /// <returns>A stream of the resource data if found, otherwise null</returns>
        /// <exception cref="System.ArgumentNullException">Thrown when supplied parameters are null.</exception>
        public Stream GetStream(string overrideDefaultNamespace, string resourceNamespaceStub, Assembly assembly)
        {
            #region Validate

            // Validate the parameters
            if (resourceNamespaceStub == null)
                throw new ArgumentNullException("resourceNamespaceStub");
            if (assembly == null)
                throw new ArgumentNullException("assembly");

            #endregion

            // Return the resource string from the assembly
            return
                assembly.GetManifestResourceStream(GetResourceNamespace(overrideDefaultNamespace, resourceNamespaceStub));
        }

        /// <summary>
        /// Gets the string.
        /// </summary>
        /// <param name="resourceNamespaceStub">The resource namespace stub.</param>
        /// <returns></returns>
        public string GetString(string resourceNamespaceStub)
        {
            return GetStringFromSpecificAssembly(DefaultRootNamespace, resourceNamespaceStub,
                                                 Assembly.GetExecutingAssembly());
        }

        /// <summary>
        /// Gets the string.
        /// </summary>
        /// <param name="overrideDefaultNamespace">The override default namespace.</param>
        /// <param name="resourceNamespaceStub">The resource namespace stub.</param>
        /// <returns></returns>
        public string GetString(string overrideDefaultNamespace, string resourceNamespaceStub)
        {
            string returnString = string.Empty;
            try
            {
                returnString = GetStringFromSpecificAssembly(overrideDefaultNamespace, resourceNamespaceStub,
                                                             Assembly.GetCallingAssembly());
            }
            catch (MissingManifestResourceException)
            {
                // The resource file doesn't exist for the calling assembly
                // In this case we try and load from our own (executing) assembly
                // So we do nothing and bubble the empty returnString to the if statement outside of the
                // catch block... Ugly, but meh - it works
            }

            if (string.IsNullOrEmpty(returnString))
                returnString = GetString(resourceNamespaceStub);

            return returnString;
        }

        private string GetStringFromSpecificAssembly(string defaultNamespace, string resourceNamespaceStub,
                                                     Assembly assembly)
        {
            #region Validate

            if (defaultNamespace == null)
                throw new ArgumentNullException("defaultNamespace");
            if (resourceNamespaceStub == null)
                throw new ArgumentNullException("resourceNamespaceStub");
            if (assembly == null)
                throw new ArgumentNullException("assembly");

            #endregion

            // Get the resource stream
            Stream ResourceStream = GetStream(defaultNamespace, resourceNamespaceStub, assembly);
            if (ResourceStream != null)
            {
                // Create a stream reader to convert the data to a string
                using (var Reader = new StreamReader(ResourceStream))
                {
                    // Return the data
                    return Reader.ReadToEnd();
                }
            }
            else
                return null;
        }

        #endregion

        #endregion

        /// <summary>
        /// Helper method for combining a namespace root with a supplied suffix.
        /// </summary>
        /// <param name="overrideDefaultNamespace">Namespace root</param>
        /// <param name="resourceNamespaceStub">Type namespace suffix</param>
        /// <returns>Concatenation of the two supplied parameters delimited by '.' </returns>
        public static string GetResourceNamespace(string overrideDefaultNamespace, string resourceNamespaceStub)
        {
            return string.Format("{0}.{1}", overrideDefaultNamespace.Trim('.'), resourceNamespaceStub.Trim('.'));
        }

        /// <summary>
        /// Helper method for combining a namespace root with <see cref="P:DefaultRootNamespace"/>.
        /// </summary>
        /// <param name="resourceNamespaceStub">Type namespace suffix</param>
        /// <returns>Concatenation of the supplied parameter with <see cref="P:DefaultRootNamespace"/> delimited by '.'</returns>
        public string GetResourceNamespace(string resourceNamespaceStub)
        {
            return GetResourceNamespace(DefaultRootNamespace, resourceNamespaceStub);
        }
    }
}